package de.dlrg.wms.controller.controllers.internalControllers;

import static java.lang.Math.abs;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import de.dlrg.wms.controller.controllers.AgeClassController;
import de.dlrg.wms.controller.controllers.TrackController;
import de.dlrg.wms.model.Discipline;
import de.dlrg.wms.model.Run;
import de.dlrg.wms.model.Swimmer;
import de.dlrg.wms.model.SwimmerTrackRun;
import de.dlrg.wms.model.Track;
import de.dlrg.wms.model.transformed.AgeClassToSwimmer;

@Component
public class AutoAssignSwimmerController {

	@Autowired
	private AgeClassController ageClassController;
	@Autowired
	private TrackController trackController;
	
	private List<Track> tracks;

	private List<Run> sanitifyRunNumbers(List<Run> runs) {
		int runNo = 1;
		for (Run run : runs) {
			run.setRunNo(runNo);
			runNo++;
		}
		return runs;
	}

	public List<Run> autoAssignSwimmer() {
		List<Run> autoAssignedSwimmers = new ArrayList<Run>();
		this.tracks = this.trackController.readAll();

		for (AgeClassToSwimmer ageClassToSwimmer : this.ageClassController
				.readAllAgeClassToSwimmer()) {
			Run run = new Run();
			autoAssignedSwimmers.add(run);
			int i = 1;
			Discipline selectedDiscipline = ageClassToSwimmer.getSwimmers()
					.get(0).getAgeClass().getDiscipline().get(0);
			for (Swimmer swimmer : ageClassToSwimmer.getSwimmers()) {
				run.getSwimmerTrackRun().add(
						new SwimmerTrackRun().setSwimmer(swimmer)
								.setTrack(this.tracks.get(i - 1))
								.setDiscipline(selectedDiscipline)

				);
				if (i >= this.tracks.size()) {
					run = new Run();
					autoAssignedSwimmers.add(run);
					i = 1;
				} else {
					i++;
				}
			}
			if (autoAssignedSwimmers.size() > 1) {
				Run r1 = run;
				Run r2 = autoAssignedSwimmers
						.get(autoAssignedSwimmers.size() - 2);
				this.balance(r1, r2);
			}
		}

		autoAssignedSwimmers = this.sanitifyRunNumbers(autoAssignedSwimmers);
		int numberOfRuns = autoAssignedSwimmers.size();
		List<Run> newAutoAssigned = new ArrayList<Run>();
		newAutoAssigned.addAll(autoAssignedSwimmers);
		for (Run run : autoAssignedSwimmers) {
			List<Run> clonedRuns = this.cloneRunForDiscipline(run);
			int i = 1;
			for (Run clonedRun : clonedRuns) {
				clonedRun.setRunNo(run.getRunNo() + i * numberOfRuns);
				i++;
			}
			newAutoAssigned.addAll(clonedRuns);
		}
		return newAutoAssigned;
	}

	private void balance(Run r2, Run r1) {
		int diff = abs(r1.getSwimmerTrackRun().size()
				- r2.getSwimmerTrackRun().size());
		if (diff > 1) {
			int nToTake = diff / 2;
			int startPositionTrack =
				this.tracks.indexOf(
						r2.getSwimmerTrackRun().get(r2.getSwimmerTrackRun().size() - 1).getTrack()
						) + 1;
			for (int i = (r1.getSwimmerTrackRun().size() - nToTake); i < r1
					.getSwimmerTrackRun().size(); i++) {

				SwimmerTrackRun curSwimmerTrackRun = r1.getSwimmerTrackRun()
						.get(i);
				curSwimmerTrackRun.setTrack(this.tracks.get(startPositionTrack));
				r1.getSwimmerTrackRun().remove(curSwimmerTrackRun);
				r2.getSwimmerTrackRun().add(curSwimmerTrackRun);

				startPositionTrack++;
			}
		}
	}

	private List<Run> cloneRunForDiscipline(Run prototype) {
		List<Run> clonedRuns = new ArrayList<Run>();

		for (int j = 1; j < neededRunsByDiscipline(prototype); j++) {
			Run newRun = new Run();
			for (SwimmerTrackRun swimmerTrackRun : prototype
					.getSwimmerTrackRun()) {
				if (j < swimmerTrackRun.getSwimmer().getAgeClass()
						.getDiscipline().size()) {

					newRun.getSwimmerTrackRun().add(
							new SwimmerTrackRun()
									.setSwimmer(swimmerTrackRun.getSwimmer())
									.setTrack(swimmerTrackRun.getTrack())
									.setDiscipline(
											swimmerTrackRun.getSwimmer()
													.getAgeClass()
													.getDiscipline().get(j)));

				}
			}
			if (newRun.getSwimmerTrackRun().size() > 0) {
				clonedRuns.add(newRun);
			}
		}
		return clonedRuns;
	}

	private int neededRunsByDiscipline(Run run) {
		int maxOfDisciplines = 0;
		for (SwimmerTrackRun swimmerTrackRun : run.getSwimmerTrackRun()) {
			int sizeOfDisciplinesOfSwimmer = swimmerTrackRun.getSwimmer()
					.getAgeClass().getDiscipline().size();
			if (sizeOfDisciplinesOfSwimmer > maxOfDisciplines) {
				maxOfDisciplines = sizeOfDisciplinesOfSwimmer;
			}
		}
		return maxOfDisciplines;
	}
}
